// Copyright (C) 2005 Taylor L. Riché <riche@cs.utexas.edu>
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// 	$Id: ApplicationGraph.cc 343 2005-11-07 03:18:09Z richetl $	
//

#include "ApplicationGraph.hh"

using namespace lee;

void
ApplicationGraph::makeConnection(std::string leftComposable, int leftPort, std::string rightComposable)
{
  Synchronized::Scope mutex(this);
  if(appGraph[rightComposable] == NULL) {
    appGraph[rightComposable] = new std::map<int, Composable*>;
  }
  while(composableMap[rightComposable] == NULL) {
    wait();
  }
  assert(appGraph[leftComposable] != NULL);
  (*appGraph[leftComposable])[leftPort] = composableMap[rightComposable];
  notify();
  return;
}

void 
ApplicationGraph::addComposableAfter(std::string origComposable, int origPort, 
			      std::string newComposable, int newPort)
{
  Synchronized::Scope mutex(this);

  while(composableMap[newComposable] == NULL) {
    wait();
  }
  if(appGraph[newComposable] == NULL) {
    appGraph[newComposable] = new std::map<int, Composable*>;
  }
  Composable * oldComposable = (*appGraph[origComposable])[origPort];
  (*appGraph[newComposable])[newPort] = oldComposable;
  (*appGraph[origComposable])[origPort] = composableMap[newComposable];
  sched->setPU(static_cast<Function *>(composableMap[newComposable]));
  return;
}

void 
ApplicationGraph::addComposableBefore(std::string newComposable, int newPort, std::string origComposable)
{
  Synchronized::Scope mutex(this);
  Composable * newSink;
  while((newSink = composableMap[newComposable]) == NULL) {
    wait();
  }
  if(appGraph[newComposable] == NULL) {
    appGraph[newComposable] = new std::map<int, Composable*>;
  }
  std::list<std::pair<std::string, int> > * srcList = 
    getSources(origComposable);
  assert(!srcList->empty()); // FIXME this needs to be handled better
  for(std::list<std::pair<std::string, int> >::iterator i = srcList->begin();
      i != srcList->end(); i++) {
    (*appGraph[i->first])[i->second] = newSink;
  }  
  Composable * origSink = composableMap[origComposable];
  assert(origSink != NULL);
  assert(appGraph[newComposable] != NULL);
  (*appGraph[newComposable])[newPort] = origSink;
  sched->setPU(static_cast<Function *>(newSink));
  return;
}

void
ApplicationGraph::deleteComposable(std::string composableName, int port)
{
  Synchronized::Scope mutex(this);
  std::list<std::pair<std::string, int> > * srcList = 
    getSources(composableName);
  Composable * newSink = (*appGraph[composableName])[port];
  assert(newSink != NULL); //FIXME handle this
  for(std::list<std::pair<std::string, int> >::iterator i = srcList->begin();
      i != srcList->end(); i++) {
    (*appGraph[i->first])[i->second] = newSink;
  }
  composableMap.erase(composableName);
  assert(composableMap.count(composableName) == 0);
  return;
}

std::list<std::pair<std::string, int> > * 
ApplicationGraph::getSources(std::string const & sink)
{
  // lock already held, no need to grab new one
  std::list<std::pair<std::string, int> > * retList = 
    new std::list<std::pair<std::string, int> >;
  Composable * sinkPtr = composableMap[sink];
  for(std::map<std::string, std::map<int, Composable*> *>::iterator i = appGraph.begin();
      i != appGraph.end(); i++) {
    for(std::map<int, Composable *>::iterator j = i->second->begin();
	j != i->second->end(); j++) {
      if(j->second == sinkPtr) {
	std::pair<std::string, int> newPair;
	newPair.first = i->first;
	newPair.second = j->first;
	retList->push_back(newPair);
      }
    }
  }
  return retList;
}
